﻿package taurus.client.io;

import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.InetSocketAddress;
import java.net.Socket;
import java.net.SocketAddress;

import com.taurus.core.util.ByteArray;

import taurus.client.NetManager;
import taurus.client.SocketCode;

/**
 * Tcp协议 Socket核心类
 * @author daixiwei	daixiwei15@126.com
 *
 */
class SocketTcp extends ISocket {
	private static final int	READ_BUFFER_SIZE	= 0x1000;
	private byte[]				_byteBuffer			= new byte[READ_BUFFER_SIZE];

	private Socket				_connection;
	private final Object		_syncer				= new Object();

	public SocketTcp(SocketEngine npeer) {
		super(npeer);
	}

	@Override
	public boolean connect() {
		if (!super.connect()) {
			return false;
		} else {
			state = SocketState.Connecting;
			Thread t = new Thread(new Runnable() {
				@Override
				public void run() {
					connectThread();
				}
			});
			t.setName("mpnet connect thread");
			t.start();
			return true;
		}
	}

	private void connectThread() {
		try {
			SocketAddress remoteAddr = new InetSocketAddress(serverAddress, super.serverPort);
			_connection = new Socket();
			_connection.setTcpNoDelay(true);
			_connection.connect(remoteAddr, NetManager.TIMEOUT_TIME * 1000);
			state = SocketState.Connected;
			_engine.enqueueStatusCallback(SocketCode.Connect);
		} catch (Exception ex) {
			try {
				if (_connection != null)
					_connection.close();
			} catch (Exception e) {
				logger.error("Connection close error", e);
			}
			logger.error("Connection error", ex);
			handleException(SocketCode.ExceptionOnConnect);
			return;
		}
		Thread _thrSocketReader = new Thread(new Runnable() {
			@Override
			public void run() {
				__read();
			}
		});
		_thrSocketReader.start();
	}

	@Override
	public boolean disconnect() {
		if (state == SocketState.Disconnected)
			return false;
		state = SocketState.Disconnecting;
		synchronized (_syncer) {
			if (_connection != null) {
				try {
					_connection.close();
				} catch (Exception ex) {
					logger.error("Disconnect error", ex);
				}
				_connection = null;
			}
		}
		state = SocketState.Disconnected;
		return true;
	}

	@Override
	public boolean send(byte[] data) {
		return __writeSocket(data);
	}

	private boolean __writeSocket(byte[] buf) {
		if (_connection == null || state != SocketState.Connected) {
			logger.error("Trying to write to disconnected socket");
			return false;
		} else {
			try {
				OutputStream outStream = _connection.getOutputStream();
				outStream.write(buf);
				outStream.flush();
			} catch (Exception exception2) {
				logger.error("General error writing to socket", exception2);
				handleException(SocketCode.SendError);
				return false;
			}
		}
		return true;
	}

	private void __read() {
		int size = 0;
		while (state == SocketState.Connected) {
			try {
				try {
					InputStream inputStream = _connection.getInputStream();
					size = inputStream.read(_byteBuffer);
				} catch (Exception ex) {
					throw new TSocketException(SocketCode.ExceptionOnReceive);
				}
				if (size == 0 || size == -1) {
					throw new TSocketException(SocketCode.DisconnectByServer);
				}
				__handleBinaryData(_byteBuffer, size);

			} catch (TSocketException ex3) {
				if (state != SocketState.Disconnecting && state.ordinal() > SocketState.Disconnected.ordinal()) {
					logger.error("Receiving failed.: ", ex3);
					super.handleException(ex3.socketCode);
				}
			} catch (Exception ex4) {
				if (state != SocketState.Disconnecting && state.ordinal() > SocketState.Disconnected.ordinal()) {
					logger.error("Receiving Exception: ", ex4);
					super.handleException(SocketCode.ExceptionOnReceive);
				}
			}
		}

		this.disconnect();
	}

	private void __handleBinaryData(byte[] buf, int size) throws IOException {
		byte[] dst = new byte[size];
		System.arraycopy(buf, 0, dst, 0, size);
		ByteArray array = new ByteArray(dst);
		_engine._packetHandler.onDataRead(array);
	}
}
